package uk.ac.aber.paws.client.db;

import uk.ac.aber.paws.client.constants.Constants;
import uk.ac.aber.paws.client.core.*;
import uk.ac.aber.paws.client.net.NetworkChecks;
import uk.ac.aber.paws.client.net.SendMessage;
import uk.ac.aber.paws.client.state.LoginInfo;
import static uk.ac.aber.paws.client.constants.Constants.*;

import java.sql.Timestamp;
import java.util.*;
import java.util.logging.Level;

/**
 * @author Rob Johnson, Aberystwyth University
 * @email rob.johnson@aber.ac.uk
 * @date 16th August 2011
 * 
 * DBLogger.java
 * 
 * This class is used to perform calls in order to perform database operations.
 * 
 * a) A DBCall is passed in along with various parameters for teh database call
 * b) The DBCall is sent via ServerSockets to the server
 * c) The response is parsed, if necessary and the relevant data returned to the caller
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

public class DBLogger {

	/**
	 * This procedure takes the DBCall object and sends to the PAWS Server service via
	 * use of the ServerSocket. If a failure has occurred, then the DBCall is added to a 
	 * caching queue to be reattempted later.
	 * @param dbCall The database call object containing the information to be sent to PAWS Server
	 * @return The response from the PAWSserver socket, null if the call was rejected or failed 
	 */
	private Object makeDBCall(DBCall dbCall) {
		
		Object returnObj = null;
		
		try {
			
			int count = 0;
			
			// check for successful internet connection (important following wake up...
			/*while (!NetworkChecks.checkNetworkIsUp()) {
				Initiator.LOG_FILE.log(Level.ALL,"Network interface down....waiting");
				Thread.currentThread().sleep(500);
				
				count++;
				
				if (count > 600) {
					Initiator.LOG_FILE.log(Level.ALL,"Interface card non responsive!");
					break;
				}
			}*/
	
			if ((returnObj = SendMessage.sendDBCall(dbCall)) == null) {
				
				// if waiting too long, store the dbCall for later
				PAWSClient.SYSTEM_STATE.addCachedDBCall(dbCall);
				return null;
			}
		
			return returnObj;
		}
		catch (Exception ex) {
			ex.printStackTrace();
			return null;
		}
		
	}

	/**
	 * Registers a power management event to the database utilising powerman_register_event stored procedure.
	 * @param computerName The client computer name being logged
	 * @param event The event string such as SLEEP, WAKE etc
	 * @return Returns true if database commit was successful
	 */
	public boolean registerPowerEvent(String computerName, String event, Timestamp dateStamp) {
		
		Vector<Object> params = new Vector<Object>();
		
		params.add(computerName);
		params.add(event);
		params.add(dateStamp);
		DBCall dbCall = new DBCall(DBCall.UPDATE,"PowerEventLogs_Add",params,false,false);
		
		if (makeDBCall(dbCall) == null) {
			return false;
		}
	
		return true;
	}
	
	/**
	 * On computer startup, searches for a stale user account session in the database, should it be null, amends to close
	 * the session as 1 hour. This ensures the server when client computers are genuinely logged in. Calls 
	 * stored procedure stats_fix_last_active_login
	 * @param computerName The client computer name being logged. 
	 */
	public void fixLogoutForComputer(String computerName) {
		
		Vector<Object> params = new Vector<Object>();
		
		Initiator.LOG_FILE.log(Level.ALL,"Fixing logout sessions");
		
		params.add(computerName);
		DBCall dbCall = new DBCall(DBCall.UPDATE,"Sessions_FixUnClosed",params,false,false);
		
		makeDBCall(dbCall);
		
	}
	
	
	/**
	 * When a user has automatically been forced a logout, this is recorded to the database by calling  
	 * paws_new_auto_logout stored procedure
	 * @param computerName The computer name of this client
	 * @param userName The user id which has been forcibly logged out
	 */
	public void registerForcedAutoLogoutEvent(String computerName, String userName) {
	
		Vector<Object> params = new Vector<Object>();
		
		params.add(computerName);
		params.add(userName);
		params.add(new Timestamp(Calendar.getInstance().getTimeInMillis()));
		DBCall dbCall = new DBCall(DBCall.UPDATE,"AutoLogouts_Add",params,false,false);
		
		makeDBCall(dbCall);
		
	}
	
	/**
	 * When a user has left a pen drive in the system, the event is logged using this
	 * procedure.
	 * @param computerName The computer name of this client
	 * @param userName The user id which has been forcibly logged out
	 */
	public void registerPendriveEvent(String computerName, String userName) {
		
		Vector<Object> params = new Vector<Object>();
		
		params.add(computerName);
		params.add(userName);
		params.add(new Timestamp(Calendar.getInstance().getTimeInMillis()));
		DBCall dbCall = new DBCall(DBCall.UPDATE,"PenDriveEvents_Set",params,false,false);
		
		makeDBCall(dbCall);
		
	}
	
	/**
	 * When a user is logged out, the entry in the database for the login session is updated with the 
	 * logout time. Calls stats_close_session stored procedure
	 * @param sessionID This is the id originally returned from the database when the login session was first created in the DB.
	 */
	public void registerUserLogout(long sessionID) {
		
		Vector<Object> params = new Vector<Object>();
		
		Initiator.LOG_FILE.log(Level.INFO,new Long(sessionID).toString());
		
		params.add(sessionID);
		params.add(new Timestamp(Calendar.getInstance().getTimeInMillis()));
		DBCall dbCall = new DBCall(DBCall.UPDATE,"Sessions_Close",params,false,false);
		
		makeDBCall(dbCall);
	}
	
	/**
	 * When a user logs in, this function is called to log the new entry into the database. Calls 
	 * the paws_new_login_session stored procedure.
	 */
	public void registerNetworkCreds() {
	
		Vector<Object> params = new Vector<Object>();
		
		params.add(PAWSClient.SYSTEM_STATE.getComputerInfo().getComputerName());
		params.add(PAWSClient.SYSTEM_STATE.getComputerInfo().getMacAddress());
		params.add(PAWSClient.SYSTEM_STATE.getComputerInfo().getIpAddress());
		params.add(Constants.VERSION);
		
		DBCall dbCall = new DBCall(DBCall.UPDATE,SERVERCALL_REGISTER_NETWORK_CREDS,params,false,true);
		
		makeDBCall(dbCall);
	}
	
	/**
	 * When a user logs in, this function is called to log the new entry into the database. Calls 
	 * the paws_new_login_session stored procedure.
	 */
	public long registerNewUserLogin(String userID, long loginTime) {
	
		long returnVal = -1l;
	
		Vector<Object> params = new Vector<Object>();
		
		params.add(PAWSClient.SYSTEM_STATE.getComputerInfo().getComputerName());
		params.add(userID);
		params.add(PAWSClient.SYSTEM_STATE.getLanguage());
		params.add(PAWSClient.SYSTEM_STATE.getComputerInfo().getOsBuild());
		params.add(PAWSClient.SYSTEM_STATE.getComputerInfo().getDomain());
		params.add(PAWSClient.SYSTEM_STATE.getComputerInfo().getSerialNumber());
		params.add(PAWSClient.SYSTEM_STATE.getComputerInfo().getPawsVersion());
		params.add(PAWSClient.SYSTEM_STATE.getComputerInfo().getOperatingSystem());
		params.add(new Timestamp(loginTime));
		
		DBCall dbCall = new DBCall(DBCall.QUERY,SERVERCALL_NEW_LOGIN_SESSION,params,true,true);
		
		DBCall response = (DBCall)makeDBCall(dbCall);
	
		try {
			
			Hashtable<String,Object> responseObj = response.getResponse();
			
			@SuppressWarnings("unchecked")
			Vector<Hashtable<String,Object>> results = (Vector<Hashtable<String,Object>>)responseObj.get("QUERYRESULT");
			
			if (results != null) {
				Initiator.LOG_FILE.log(Level.INFO,results.toString());
			
				returnVal = ((Long)results.get(0).get("SESSION_ID")).longValue();
			}
			
		}
		catch (Exception e) {
			e.printStackTrace();
			return -1;
		}
		
		return returnVal;
	}	
	
}
